package com.tuanzi.loan.workflow.listener;

import java.util.Map;

import org.activiti.engine.EngineServices;
import org.activiti.engine.delegate.event.ActivitiEntityEvent;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.delegate.event.impl.ActivitiActivityEventImpl;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.google.common.collect.Maps;
import com.tuanzi.loan.business.entity.CurrentUser;
import com.tuanzi.loan.workflow.common.ActivityType;
import com.tuanzi.loan.workflow.entity.ScriptTask;
import com.tuanzi.loan.workflow.entity.ScriptTaskType;
import com.tuanzi.loan.workflow.service.ScriptTaskService;
import com.tuanzi.loan.workflow.service.WorkflowProcessorService;

import lombok.extern.slf4j.Slf4j;

/**
 * @author Ray
 * @date 2018/01/17
 */
@Slf4j
@Component
public class ScriptEventListener implements ActivitiEventListener {

	@Autowired
	private ScriptTaskService scriptTaskService;
	@Autowired
	private ApplicationContext applicationContext;

	@Override
	public void onEvent(ActivitiEvent event) {
		try {

			switch (event.getType()) {
				case ACTIVITY_STARTED:
					// 调用前置任务
					this.executeActivityScript(event, ScriptTaskType.PRE);
					break;
				case TASK_ASSIGNED:
					// 调用前置任务
					this.executeTaskScript(event, ScriptTaskType.PRE);
					break;
				case TASK_COMPLETED:
					// 调用后置任务
					this.executeTaskScript(event, ScriptTaskType.POST);
					break;
				case ACTIVITY_COMPLETED:
					// 调用后置任务
					this.executeActivityScript(event, ScriptTaskType.POST);
					break;
				default:
					// 不处理其他事件
					break;
			}

		} catch (Exception e) {
			log.error("流程脚本调用错误 procDefId : {} , procInstId : {} , eventType : {} , executionId : {} , errorMessage : {} ", event.getProcessDefinitionId(),
					event.getProcessInstanceId(), event.getType(), event.getExecutionId(), e.getMessage());
			throw e;
		}
	}

	@Override
	public boolean isFailOnException() {
		return true;
	}

	/**
	 * 处理userTask脚本
	 *
	 * @param event
	 */
	private void executeTaskScript(ActivitiEvent event, ScriptTaskType scriptTaskType) {
		EngineServices engineServices = event.getEngineServices();
		String procDefId = event.getProcessDefinitionId();
		ProcessDefinition processDefinition = engineServices.getRepositoryService().getProcessDefinition(procDefId);
		ProcessInstance processInstance = engineServices.getRuntimeService().createProcessInstanceQuery().processInstanceId(event.getProcessInstanceId()).singleResult();
		ActivitiEntityEvent entityEvent = (ActivitiEntityEvent) event;
		Task task = (Task) entityEvent.getEntity();
		String activityId = task.getTaskDefinitionKey();

		ScriptTask scriptTask = scriptTaskService.findByProcDefIdAndActivityIdAndTaskType(procDefId, activityId, scriptTaskType);
		if (scriptTask == null) {
			log.info("没找到对应的任务 procDefId : {} , activityId : {} , scriptTaskType : {} ", procDefId, activityId, scriptTaskType);
			return;
		}

		Map<String, Object> taskVariables = Maps.newHashMap();
		if (task != null) {
			taskVariables = engineServices.getTaskService().getVariables(task.getId());
		}

		Map<String, Object> parameters = Maps.newHashMap();
		parameters.put("applicationContext", applicationContext);
		parameters.put("workflowProcessorService", applicationContext.getBean(WorkflowProcessorService.class));
		parameters.put("procInst", processInstance);
		parameters.put("procInstId", processInstance.getId());
		parameters.put("procDef", processDefinition);
		parameters.put("procDefId", processDefinition.getId());
		parameters.put("businessKey", processInstance.getBusinessKey());
		parameters.put("user", CurrentUser.get().getAccount());
		parameters.put("task", task);
		parameters.put("taskVaiables", taskVariables);

		scriptTaskService.runScript(scriptTask, parameters);

	}

	/**
	 * 处理activity脚本
	 *
	 * @param event
	 */
	private void executeActivityScript(ActivitiEvent event, ScriptTaskType scriptTaskType) {
		ActivitiActivityEventImpl entityEvent = (ActivitiActivityEventImpl) event;
		if (ActivityType.USER_EVENT.getValue().equals(entityEvent.getActivityType())) {
			return;
		}

		EngineServices engineServices = event.getEngineServices();
		String procDefId = event.getProcessDefinitionId();
		ProcessDefinition processDefinition = engineServices.getRepositoryService().getProcessDefinition(procDefId);
		ProcessInstance processInstance = engineServices.getRuntimeService().createProcessInstanceQuery().processInstanceId(event.getProcessInstanceId()).singleResult();

		if (processInstance == null) {
			// 目前只有在启动流程的时候，流程实例为空
			HistoricProcessInstance hisProcInst = engineServices.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(event.getProcessInstanceId())
					.singleResult();
			if (hisProcInst != null) {
				BeanUtils.copyProperties(hisProcInst, processInstance);
			}
		}

		ScriptTask scriptTask = scriptTaskService.findByProcDefIdAndActivityIdAndTaskType(procDefId, entityEvent.getActivityId(), scriptTaskType);
		if (scriptTask == null) {
			log.info("没找到对应的任务 procDefId : {} , activityId : {} , scriptTaskType : {} ", procDefId, entityEvent.getActivityId(), scriptTaskType);
			return;
		}

		TaskQuery taskQuery = engineServices.getTaskService().createTaskQuery();
		Task task = taskQuery.executionId(entityEvent.getExecutionId()).taskDefinitionKey(entityEvent.getActivityId()).singleResult();

		Map<String, Object> taskVariables = null;
		if (task != null) {
			taskVariables = engineServices.getTaskService().getVariables(task.getId());
		}

		Map<String, Object> parameters = Maps.newHashMap();
		parameters.put("applicationContext", applicationContext);
		parameters.put("workflowProcessorService", applicationContext.getBean(WorkflowProcessorService.class));
		parameters.put("procInst", processInstance);
		parameters.put("procInstId", processInstance.getId());
		parameters.put("procDef", processDefinition);
		parameters.put("procDefId", processDefinition.getId());
		parameters.put("businessKey", processInstance.getBusinessKey());
		parameters.put("user", CurrentUser.get().getAccount());
		parameters.put("task", task);
		parameters.put("taskVaiables", taskVariables);

		scriptTaskService.runScript(scriptTask, parameters);
	}
}
